// file:kernel/fs/fsal/fd.c
// autor:jiangxinpeng
// time:2021.3.4
// copyright:(C) by jiangxinpeng,All right are reserved.

#include <os/fsal.h>
#include <os/fsalif.h>
#include <os/fs.h>
#include <os/fat.h>
#include <os/memcache.h>
#include <os/task.h>
#include <os/driver.h>
#include <os/schedule.h>
#include <os/spinlock.h>
#include <os/fd.h>
#include <lib/type.h>
#include <lib/string.h>
#include <lib/stddef.h>

// init targe task file descript
int FsFdInit(task_t *task)
{
    if (task != NULL)
    {
        // create file manage
        task->fileman = KMemAlloc(sizeof(file_manager_t));
        if (task->fileman != NULL)
        {
            for (int i = 0; i < LOCAL_FILE_MAX; i++)
            {
                task->fileman->fd[i].flags = 0;
                task->fileman->fd[i].fsal = NULL;
                task->fileman->fd[i].offset = 0;
                task->fileman->fd[i].handle = -1;
            }
        }
        memset(task->fileman->cwd, 0, MAX_PATH_LEN);
        strcpy(task->fileman->cwd, "/");
        SpinLockInit(&task->fileman->lock);
        return 0;
    }
    return -1;
}

int FsFdExit(task_t *task)
{
    if (task != NULL)
    {
        if (task->fileman != NULL)
        {
            for (int i = 0; i < LOCAL_FILE_MAX; i++)
            {
                SysClose(i);
            }
            KMemFree(task->fileman);
            task->fileman = NULL;
            return 0;
        }
    }
    return -1;
}

int FsFdCopy(task_t *des, task_t *src)
{
    if (src->fileman != NULL && des->fileman != NULL)
    {
        SpinLockDisInterrupt(&des->fileman->lock);
        strcpy(des->fileman->cwd, src->fileman->cwd);
        for (int i = 0; i < LOCAL_FILE_MAX; i++)
        {
            if (src->fileman->fd[i].flags)
            {
                des->fileman->fd[i].flags = src->fileman->fd[i].flags;
                des->fileman->fd[i].handle = src->fileman->fd[i].handle;
                des->fileman->fd[i].fsal = src->fileman->fd[i].fsal;
                des->fileman->fd[i].offset = src->fileman->fd[i].offset;
                FsIfIncRef(i);
            }
        }
        SpinUnlockEnInterrupt(&des->fileman->lock);
        return 0;
    }
    return -1;
}

int FsFdCopyOnly(task_t *src, task_t *des)
{
    if (src->fileman != NULL && des->fileman != NULL)
    {
        SpinLockDisInterrupt(&des->fileman->lock);
        strcpy(des->fileman->cwd, src->fileman->cwd);
        for (int i = 0; i < LOCAL_FILE_MAX; i++)
        {
            if (i >= 3)
                break;

            if (src->fileman->fd[i].flags)
            {
                des->fileman->fd[i].flags = src->fileman->fd[i].flags;
                des->fileman->fd[i].handle = src->fileman->fd[i].handle;
                des->fileman->fd[i].fsal = src->fileman->fd[i].fsal;
                des->fileman->fd[i].offset = src->fileman->fd[i].offset;
                FsIfIncRef(i);
            }
        }
        SpinUnlockEnInterrupt(&des->fileman->lock);

        return 0;
    }
    return -1;
}

file_fd_t *FdToFile(int fd_idx)
{
    task_t *cur = cur_task;

    if (OUT_RANGE(fd_idx, 0, LOCAL_FILE_MAX))
        return NULL;
    return cur_task->fileman->fd + fd_idx;
}

int HandleToFd(int handle, uint32_t flags)
{
    task_t *cur = cur_task;

    file_fd_t *fd;
    for (int i = 0; i < LOCAL_FILE_MAX; i++)
    {
        fd = &cur_task->fileman->fd[i];
        if (fd->handle == handle && fd->flags == flags)
            return i;
    }
    return -1;
}

int FsFdAlloc(int base)
{
    task_t *cur = cur_task;

    SpinLockDisInterrupt(&cur->fileman->lock);
    for (int i = base; i < LOCAL_FILE_MAX; i++)
    {
        if (!cur->fileman->fd[i].flags)
        {
            cur->fileman->fd[i].flags = FILE_FD_ALLOC;
            cur->fileman->fd[i].handle = -1;
            cur->fileman->fd[i].offset = 0;
            cur->fileman->fd[i].fsal = NULL;
            SpinUnlockEnInterrupt(&cur->fileman->lock);
            return i;
        }
    }
    SpinUnlockEnInterrupt(&cur->fileman->lock);
    return -1;
}

int FsFdFree(int idx)
{
    task_t *task = cur_task;

    if (OUT_RANGE(idx, 0, LOCAL_FILE_MAX))
        return -1;
    if (!task->fileman->fd[idx].flags) // may be has been closed
        return 0;
    SpinLockDisInterrupt(&task->fileman->lock);
    task->fileman->fd[idx].flags = 0;
    task->fileman->fd[idx].handle = -1;
    task->fileman->fd[idx].offset = 0;
    task->fileman->fd[idx].fsal = NULL;
    SpinUnlockEnInterrupt(&task->fileman->lock);
    return 0;
}

void FsFdSetFsal(file_fd_t *fd, uint32_t flags)
{
    switch (flags)
    {
    case FILE_FD_KERNEL:
        fd->fsal = &fsif;
        break;
    case FILE_FD_DEVICE:
        fd->fsal = &devfs_fsal;
        break;
    case FILE_FD_PIPE0:
        fd->fsal = &pipeif_rd;
        break;
    case FILE_FD_PIPE1:
        fd->fsal = &pipeif_wr;
        break;
    default:
        fd->fsal = NULL;
        break;
    }
}

static void _FsFdInstall(int resid, int newid, int flags)
{
    task_t *cur = cur_task;
    if (!cur)
        return;
    SpinLockDisInterrupt(&cur->fileman->lock);
    cur->fileman->fd[newid].flags |= flags;
    cur->fileman->fd[newid].handle = resid;
    cur->fileman->fd[newid].offset = 0;
    cur->fileman->fd[newid].fsal = NULL;
    FsFdSetFsal(&cur->fileman->fd[newid], flags);
    SpinUnlockEnInterrupt(&cur->fileman->lock);
}

int FsFdInstallTo(int resid, int newid, int flags)
{
    if (OUT_RANGE(resid, 0, LOCAL_FILE_MAX))
        return -1;
    if (OUT_RANGE(newid, 0, LOCAL_FILE_MAX))
        return -1;
    _FsFdInstall(resid, newid, flags);
    return newid;
}

int FsFdInstall(int resid, int flags)
{
    int newid;
    if (OUT_RANGE(resid, 0, LOCAL_FILE_MAX))
        return -1;
    newid = FsFdAlloc(0);
    _FsFdInstall(resid, newid, flags);
    return newid;
}

int FsFdInstallBase(int resid, int flags, int base)
{
    int newid;
    if (OUT_RANGE(resid, 0, LOCAL_FILE_MAX))
        return -1;
    newid = FsFdAlloc(base);
    _FsFdInstall(resid, newid, flags);
    return newid;
}

int FsFdUnInstall(int id)
{
    return FsFdFree(id);
}
